#include <signal.h>
#include <unistd.h>

#include <cstring>
#include <ctime>
#include <iostream>
#include <string>

using namespace std;

static ostream& printSigset(ostream& out, const string& prefix,
                            const sigset_t* sigset) {
    int sigCnt = 0;

    for (int sig = 1; sig < NSIG; sig++) {
        if (sigismember(sigset, sig)) {
            sigCnt++;
            out << prefix << sig << " (" << strsignal(sig) << ")" << endl;
        }
    }

    if (sigCnt == 0) {
        out << prefix << "<empty signal set>" << endl;
    }
    return out;
}

static ostream& printSigMask(ostream& out, const string& msg) {
    sigset_t mask;

    if (!msg.empty()) {
        out << msg;
    }

    if (sigprocmask(SIG_SETMASK, nullptr, &mask) == -1) {
        perror("sigprocmask");
        return out;
    }

    return printSigset(out, "\t\t", &mask);
}

static ostream& printPendingSigs(ostream& out, const string& msg) {
    sigset_t pendingSigs;

    if (!msg.empty()) {
        out << msg;
    }

    if (sigpending(&pendingSigs) == -1) {
        perror("sigpending");
        return out;
    }

    return printSigset(out, "\t\t", &pendingSigs);
}

static volatile sig_atomic_t gotSigQuit = 0;

static void handler(int sig) {
    // UNSAFE
    cout << "Caught signal " << sig << " (" << strsignal(sig) << ")" << endl;

    if (sig == SIGQUIT) {
        gotSigQuit = 1;
    }
}

int main(int argc, char* argv[]) {
    printSigMask(cout, "Initial signal mask is:\n");

    sigset_t blockMask, origMask;
    sigemptyset(&blockMask);
    sigaddset(&blockMask, SIGINT);
    sigaddset(&blockMask, SIGQUIT);
    if (sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1) {
        perror("sigprocmask - SIG_BLOCK");
        return 1;
    }

    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags   = 0;
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, nullptr) == -1) {
        perror("sigaction");
        return 1;
    }
    if (sigaction(SIGQUIT, &sa, nullptr) == -1) {
        perror("sigaction");
        return 1;
    }

    for (int loopNum = 1; !gotSigQuit; loopNum++) {
        cout << "=== LOOP " << loopNum << endl;

        // Simulate a critical section by delaying a few seconds
        printSigMask(cout, "Starting critical section, signal mask is:\n");
        for (auto startTM = time(nullptr); time(nullptr) < startTM + 4;) {
            // Run for a few seconds elapsed time
            continue;
        }

        printPendingSigs(cout, "Before sigsuspend() - pending signals:\n");

        if (sigsuspend(&origMask) == -1 && errno != EINTR) {
            perror("sigsuspend");
            return 1;
        }
    }

    if (sigprocmask(SIG_SETMASK, &origMask, nullptr) == -1) {
        perror("sigprocmask - SIG_SETMASK");
        return 1;
    }

    printSigMask(cout, "=== Exited loop\nRestored signal mask to:\n");

    // Do other processing
    return 0;
}
